package cc.shacocloud.mirage.restful.example;

import cc.shacocloud.mirage.restful.*;
import cc.shacocloud.mirage.restful.bind.annotation.*;
import cc.shacocloud.mirage.restful.bind.validation.BindingResult;
import cc.shacocloud.mirage.restful.bind.validation.Validated;
import cc.shacocloud.mirage.restful.bind.validation.errors.BindingResultError;
import cc.shacocloud.mirage.restful.exception.BindingException;
import cc.shacocloud.mirage.restful.exception.MethodArgumentNotValidException;
import cc.shacocloud.mirage.restful.http.MultipartFile;
import cc.shacocloud.mirage.restful.http.MultipartFileUpload;
import cc.shacocloud.mirage.utils.reflection.DefaultParameterNameDiscoverer;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServerOptions;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.function.Function;

public class ExampleServer {
    
    public static void main(String[] args) {
        Vertx vertx = Vertx.vertx();
        startServer(vertx, new ExampleController());
    }
    
    /**
     * 启动样例服务
     */
    public static void startServer(Vertx vertx,
                                   Object... handlers) {
        startServer(vertx, 8080, handlers);
    }
    
    /**
     * 启动样例服务
     */
    public static void startServer(Vertx vertx,
                                   int port,
                                   Object... handlers) {
        startServer(vertx, port, vertx1 -> new MirageRequestMappingHandler(vertx1, new DefaultParameterNameDiscoverer()), handlers);
    }
    
    /**
     * 启动样例服务
     */
    public static void startServer(Vertx vertx,
                                   int port,
                                   Function<Vertx, MirageRequestMappingHandler> mappingHandlerFunc,
                                   Object @NotNull ... handlers) {
        // 构建 RequestMapping
        MirageRequestMappingHandler requestMappingHandler = buildRouterMappingHandler(vertx, mappingHandlerFunc);
        
        for (Object handler : handlers) {
            requestMappingHandler.detectHandlerMethods(handler);
        }
        
        CountDownLatch downLatch = new CountDownLatch(1);
        
        HttpServerOptions serverProperties = new HttpServerOptions();
        serverProperties.setLogActivity(false);
        serverProperties.setPort(port);
        
        // 启动服务
        VertxRouterDispatcherOptions routerDispatcherOptions = new VertxRouterDispatcherOptions();
        routerDispatcherOptions.setServerOptions(serverProperties);
        routerDispatcherOptions.setRouterMappingHandlers(
                Arrays.asList(requestMappingHandler)
        );
        VertxRouterDispatcherHandler.createHttpServer(vertx, routerDispatcherOptions)
                .onComplete(ar -> {
                    downLatch.countDown();
                    
                    if (ar.failed()) throw new RuntimeException(ar.cause());
                });
        
        // 阻塞等待初始化完成
        try {
            downLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 构建路由映射处理器
     */
    public static @NotNull MirageRequestMappingHandler buildRouterMappingHandler(Vertx vertx,
                                                                                 @NotNull Function<Vertx, MirageRequestMappingHandler> mappingHandlerFunc) {
        
        // 绑定请求处理映射器
        MirageRequestMappingHandler mirageRequestMappingHandler = mappingHandlerFunc.apply(vertx);
        
        // 异常处理器
        ExceptionHandlerExceptionResolver exceptionResolver = mirageRequestMappingHandler.getExceptionHandlerExceptionResolver();
        exceptionResolver.registerExceptionHandler(new DefaultExceptionHandler());
        
        // 初始化
        mirageRequestMappingHandler.init();
        return mirageRequestMappingHandler;
    }
    
    // ------------------ 业务代码
    
    /**
     * 默认异常处理器
     */
    @ResponseBody
    public static class DefaultExceptionHandler {
        
        @ExceptionHandler(Exception.class)
        public Future<Map<String, Object>> defaultEx(@NotNull Exception e, @NotNull HttpResponse response) {
            Map<String, Object> result = new HashMap<>();
            result.put("code", "1010");
            result.put("message", "服务器错误！");
            result.put("ex", e.getMessage());
            return Future.succeededFuture(result);
        }
        
        /**
         * 参数校验违规异常处理方法
         * <p>
         * 方法参数处理示例，便于查看
         */
        @ExceptionHandler({MethodArgumentNotValidException.class, BindingException.class})
        public Future<Object> constraintViolation(Exception e, HttpResponse response) {
            BindingResult bindingResult;
            if (e instanceof BindingException) {
                bindingResult = ((BindingException) e).getBindingResult();
            } else if (e instanceof MethodArgumentNotValidException) {
                bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
            } else {
                Map<String, Object> result = new HashMap<>();
                result.put("code", "1010");
                result.put("message", "服务器错误！");
                result.put("ex", e.getMessage());
                return Future.succeededFuture(result);
            }
            
            // 循环错误字段消息
            Map<String, List<Map<String, Object>>> fieldMap = new HashMap<>();
            for (BindingResultError bindingResultError : bindingResult.getAllErrors()) {
                
                String messageCode = bindingResultError.getCode();
                
                Map<String, Object> resp = new HashMap<>();
                resp.put("code", "invalid");
                resp.put("message", messageCode);
                resp.put("rejectedValue", bindingResultError.getRejectedValue());
                fieldMap.computeIfAbsent(bindingResultError.getNestedPath(), k -> new ArrayList<>(2)).add(resp);
            }
            
            response.setStatusCode(HttpResponseStatus.UNPROCESSABLE_ENTITY.code());
            
            Map<String, Object> obj = new HashMap<>();
            obj.put("message", HttpResponseStatus.UNPROCESSABLE_ENTITY.reasonPhrase());
            obj.put("fields", fieldMap);
            return Future.succeededFuture(obj);
        }
        
        
    }
    
    /**
     * 示例控制器
     */
    @RestController
    @RequestMapping("/test")
    public static class ExampleController {
        
        @GetMapping("/demo")
        public Future<String> demo() {
            return Future.succeededFuture("hello word! mirage...");
        }
        
        @GetMapping("/get")
        public Future<String> get(@QueryParam("name") String name) {
            return Future.succeededFuture(name);
        }
        
        @GetMapping("/mirage/:version")
        public Future<Integer> version(@PathVariable("version") Integer version) {
            return Future.succeededFuture(version);
        }
        
        @RequestMapping("/ex")
        public Future<Void> ex() {
            int a = 1 / 0; // 模拟异常状态
            return Future.succeededFuture();
        }
        
        @PostMapping("/validation")
        public Future<ValidationBean> validationBean(@RequestBody @Validated ValidationBean bean) {
            // 如果需要即时抛出参数检查异常 请去掉当前方法的 BindingResult 参数
            // 如果需要在方法内做特殊判断，可以使用 BindingResult 处理完在抛出
            return Future.succeededFuture(bean);
        }
        
        @GetMapping("/queryParams")
        public Future<ValidationBean> queryParams(@QueryParam("bean") @Validated ValidationBean bean) {
            return Future.succeededFuture(bean);
        }
        
        @PostMapping("/form")
        public Future<String> form(@FormAttribute("name") String name) {
            return Future.succeededFuture(name);
        }
        
        @PostMapping("/fileUpload")
        public Future<Object> fileUpload(@FileUpload @NotNull MultipartFileUpload fileUpload) {
            MultipartFile multipartFile = fileUpload.get();
            return multipartFile.readBuf()
                    .compose(buf -> Future.succeededFuture(buf.toString()));
        }
        
        @PostMapping("/fileUpload-withParams")
        public Future<Object> fileUploadWithParams(@FileUpload @NotNull MultipartFileUpload fileUpload,
                                                   @FormAttribute("options") String options) {
            MultipartFile multipartFile = fileUpload.get();
            return multipartFile.readBuf()
                    .compose(buf -> Future.succeededFuture(buf.toString() + ": " + options));
        }
        
        @GetMapping("/header")
        public Future<String> header(@RequestHeader(name = "host") String host) {
            return Future.succeededFuture(host);
        }
        
        @GetMapping("/restful")
        public Future<String> restfulGet() {
            return Future.succeededFuture("get");
        }
        
        @PostMapping("/restful")
        public Future<String> restfulPost() {
            return Future.succeededFuture("post");
        }
    }
    
    @Setter
    @Getter
    public static class ValidationBean {
        
        @NotBlank(message = "名称不可以为空！")
        private String name;
        
        @NotBlank(message = "值不可以为空！")
        private String value;
        
    }
    
}
